# Copyright (c) 2019 Presto Labs Pte. Ltd.
# Author: jaewon

import datetime
import hashlib
import importlib
import logging
import uuid

from coin.base.datetime_util import to_datetime
from coin.base.param_util import to_list
from coin.feed.fastfeed.feed_cache import run_from_feed_cache
from coin.strategy.mm.subscription import FeedSubscriptionRequest


def gen_run_id(uuid_hash_len=8):
  dt_str = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
  uuid_str = hashlib.sha256(uuid.uuid1().bytes).hexdigest()[:uuid_hash_len]
  return '%s-%s' % (dt_str, uuid_str)


def get_profile_module(profile_name, profile_args, ref_ts=None, **kwargs):
  if profile_name.endswith('.py'):
    profile_name = profile_name[:-3]
  profile_name = profile_name.replace('/', '.')

  module_prefix = 'coin.strategy.marketsim.profile.'
  module_name = profile_name
  if not profile_name.startswith(module_prefix):
    module_name = module_prefix + profile_name

  try:
    module = importlib.import_module(module_name)
  except ModuleNotFoundError:
    logging.fatal('Profile module <%s> not found or error occured during import', module_name)
    raise

  if 'prepare' in dir(module):
    ref_ts = to_datetime(ref_ts or datetime.datetime.now())
    module.prepare(profile_args, ref_ts, **kwargs)
  else:
    assert not profile_args, ('Profile <%s> does not support arguments' % module_name)

  return module


class _SimFeedSubsystem:
  def __init__(self):
    self.feed_sub_req = FeedSubscriptionRequest()
    self.book_reset_callbacks = []

  def subscribe_fastfeed(self, feed_sub_req, book_reset_callbacks):
    self.feed_sub_req.merge_from(feed_sub_req)
    self.book_reset_callbacks += to_list(book_reset_callbacks)


def run_sim_strategy(profile_name,
                     from_ts,
                     to_ts,
                     from_index_inclusive=None,
                     to_index_inclusive=None,
                     profile_args=None,
                     feed_cache_dir=None,
                     **kwargs):
  profile = get_profile_module(profile_name, profile_args, ref_ts=from_ts, **kwargs)

  try:
    machine = profile.get_machine()
  except TypeError:
    machine = profile.get_machine(from_ts)

  strategies = to_list(profile.get_strategy(from_ts, to_ts))
  if from_index_inclusive is not None or to_index_inclusive is not None:
    to_index = to_index_inclusive + 1 if to_index_inclusive is not None else None
    strategies = strategies[from_index_inclusive:to_index]

  sim_feed_subsystem = _SimFeedSubsystem()
  for s in strategies:
    s.prepare(sim_feed_subsystem)
    if hasattr(s, 'init'):
      s.init()

  run_from_feed_cache(sim_feed_subsystem.feed_sub_req,
                      sim_feed_subsystem.book_reset_callbacks,
                      from_ts,
                      to_ts,
                      machine=machine,
                      feed_cache_dir=feed_cache_dir)

  return [profile.get_strategy_result(s) for s in strategies]


def get_num_strategies(profile, from_ts, to_ts):
  # TODO(jaewon): Better implementation
  strategies = profile.get_strategy(from_ts, to_ts)
  return len(strategies)
